// Copyright (c) 2015-2022, Lawrence Livermore National Security, LLC.
// See top-level LICENSE file for details.

/// \file SnapshotRecord.h
/// \brief Snapshot record representation.

#pragma once

#include "common/Entry.h"

#include <algorithm>

namespace cali
{

class CaliperMetadataAccessInterface;

/// \brief Non-owning read-only view for snapshot record
class SnapshotView
{
    const Entry* m_data;
    size_t       m_len;

public:

    SnapshotView() : m_data { nullptr }, m_len { 0 } {}
    SnapshotView(size_t len, const Entry* data) : m_data { data }, m_len { len } {}
    SnapshotView(const Entry& e) : m_data { &e }, m_len { 1 } {}

    using iterator       = Entry*;
    using const_iterator = const Entry*;

    const_iterator begin() const { return m_data; }
    const_iterator end() const { return m_data + m_len; }

    size_t size() const { return m_len; }
    const Entry* data() const { return m_data; }
    bool empty() const { return m_len == 0; }

    const Entry& operator[] (std::size_t n) const { return m_data[n]; }

    Entry get(const Attribute& attr) const
    {
        for (const Entry& e : *this) {
            Entry ret = e.get(attr);
            if (!ret.empty())
                return ret;
        }

        return Entry();
    }

    Entry get_immediate_entry(const Attribute& attr) const
    {
        cali_id_t attr_id = attr.id();
        for (const Entry& e : *this) {
            if (e.node() && e.node()->id() == attr_id)
                return e;
        }

        return Entry();
    }
};

/// \brief Non-owning writable view for snapshot record
class SnapshotBuilder
{
    Entry* m_data;
    size_t m_capacity;
    size_t m_len;
    size_t m_skipped;

public:

    SnapshotBuilder() : m_data { nullptr }, m_capacity { 0 }, m_len { 0 }, m_skipped { 0 } {}
    SnapshotBuilder(size_t capacity, Entry* data)
        : m_data { data }, m_capacity { capacity }, m_len { 0 }, m_skipped { 0 }
    {}

    SnapshotBuilder(SnapshotBuilder&&)      = default;
    SnapshotBuilder(const SnapshotBuilder&) = delete;

    SnapshotBuilder& operator= (SnapshotBuilder&&)      = default;
    SnapshotBuilder& operator= (const SnapshotBuilder&) = delete;

    size_t capacity() const { return m_capacity; }
    size_t size() const { return m_len; }
    size_t skipped() const { return m_skipped; }

    void append(const Entry& e)
    {
        if (m_len < m_capacity)
            m_data[m_len++] = e;
        else
            ++m_skipped;
    }

    void append(size_t n, const Entry* entries)
    {
        size_t num_copied = std::min(n, m_capacity - m_len);
        std::copy_n(entries, num_copied, m_data + m_len);
        m_len += num_copied;
        m_skipped += n - num_copied;
    }

    void append(const Attribute& attr, const Variant& val) { append(Entry(attr, val)); }
    void append(SnapshotView view) { append(view.size(), view.data()); }

    SnapshotView view() const { return SnapshotView { m_len, m_data }; }
};

/// \brief A fixed-size snapshot record
template <std::size_t N>
class FixedSizeSnapshotRecord
{
    Entry           m_data[N];
    SnapshotBuilder m_builder;

public:

    FixedSizeSnapshotRecord() : m_builder { N, m_data } {}

    FixedSizeSnapshotRecord(const FixedSizeSnapshotRecord<N>&)               = delete;
    FixedSizeSnapshotRecord<N> operator= (const FixedSizeSnapshotRecord<N>&) = delete;

    SnapshotBuilder& builder() { return m_builder; }
    SnapshotView view() const { return m_builder.view(); }

    void reset() { m_builder = SnapshotBuilder(N, m_data); }
};

} // namespace cali
